package live.sidian.database.synchronizer.sync.structure;

import live.sidian.database.synchronizer.infrastructure.sql.SqlHelper;
import live.sidian.database.synchronizer.infrastructure.sql.SqlHelper.Record;
import live.sidian.database.synchronizer.infrastructure.utils.BeanUtil;
import live.sidian.database.synchronizer.sync.structure.model.Column;
import live.sidian.database.synchronizer.sync.structure.model.Database;
import live.sidian.database.synchronizer.sync.structure.model.Index;
import live.sidian.database.synchronizer.sync.structure.model.Table;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static live.sidian.database.synchronizer.sync.structure.model.Index.IndexType.*;

/**
 * 数据库元数据构建器
 *
 * @author sidian
 * @date 2020/3/21 20:15
 */
public class MetaDataBuilder {
    SqlHelper sqlHelper;

    private MetaDataBuilder() {
    }

    public static MetaData build(Database database) {
        try {
            MetaDataBuilder builder = new MetaDataBuilder();
            MetaData metaData = builder.doBuild(database);
            builder.sqlHelper.close();
            return metaData;
        } catch (Exception e) {
            MetaData metaData = BeanUtil.copyProperties(database, MetaData.class);
            metaData.setExist(false);
            return metaData;
        }
    }

    /**
     * 数据库元数据初始化
     */
    private MetaData doBuild(Database database) {
        // 初始化基本信息
        MetaData metaData = BeanUtil.copyProperties(database, MetaData.class);
        sqlHelper = database.toSqlHelper();
        // 初始化表信息
        List<Table> tables = initTables(metaData.getSchema());
        metaData.setTables(tables);
        // 初始化列信息
        initColumns(tables);
        // 初始化索引
        initIndex(tables);
        return metaData;
    }

    private void initIndex(List<Table> tables) {
        for (Table table : tables) { // 对于每张表
            List<Index> indexes = getIndexes(table);
            table.setIndexes(indexes);
        }
    }

    private List<Index> getIndexes(Table table) {
        List<Index> indexes = new LinkedList<>();
        // 索引与列的映射
        Map<String, List<Record>> indexMap = sqlHelper.queryList(String.format("show keys from `%s`.`%s`", table.getSchema(), table.getName()))
                .stream().collect(Collectors.groupingBy(record -> record.getString("Key_name")));
        indexMap.forEach((indexName, records) -> indexes.add(new Index(
                table.getSchema(), table.getName(), indexName,
                records.stream().map(record -> record.getString("Column_name")).collect(Collectors.toSet()),
                indexName.equals("PRIMARY") ? PRIMARY : records.get(0).getInteger("Non_unique") == 0 ? UNIQUE : INDEX)
        ));
        return indexes;
    }

    private void initColumns(List<Table> tables) {
        for (Table table : tables) {
            List<Column> columns = getColumns(table.getSchema(), table.getName());
            table.setColumns(columns);
        }
    }

    private List<Column> getColumns(String schema, String tableName) {
        // 查询列信息
        List<Record> records = sqlHelper.queryList(String.format(
                "select COLUMN_NAME,COLUMN_TYPE,IS_NULLABLE,COLUMN_DEFAULT,COLUMN_COMMENT,EXTRA "
                        + "from information_schema.columns "
                        + "where TABLE_SCHEMA='%s' and TABLE_NAME='%s' order by ORDINAL_POSITION asc ",
                schema,
                tableName
        ));
        // 转化
        List<Column> columns = records.stream().map(record ->
                Column.builder()
                        .schema(schema)
                        .table(tableName)
                        .name(record.getString("COLUMN_NAME"))
                        .type(record.getString("COLUMN_TYPE"))
                        .defaultValue(record.getString("COLUMN_DEFAULT"))
                        .nullable(record.getString("IS_NULLABLE").equals("YES"))
                        .extra(record.getString("EXTRA"))
                        .comment(record.getString("COLUMN_COMMENT"))
                        .build()
        ).collect(Collectors.toList());
        // 补充顺序
        fillOrder(columns);
        return columns;
    }

    private void fillOrder(List<Column> columns) {
        Column last = null;
        for (Column column : columns) {
            column.setAfter(last == null ? null : last.getName());
            last = column;
        }
    }

    private List<Table> initTables(String schema) {
        //获取所有表名
        List<Table> tables = sqlHelper.queryList(String.format("show tables from %s", schema)).stream()
                .map(record -> record.getString("Tables_in_" + schema))
                .map(tableName -> new Table(schema, tableName))
                .collect(Collectors.toList());
        // 填充建表语句
        for (Table table : tables) {
            Record record = sqlHelper.queryOne(String.format("show create table %s.%s", schema, table.getName()));
            table.setCreateSql(record.getString("Create Table"));
        }
        return tables;
    }
}
